@file:Suppress("BlockingMethodInNonBlockingContext")

package org.glavo.bot

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.json.*
import net.mamoe.mirai.contact.nameCardOrNick
import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.MessageSource.Key.quote
import org.glavo.bot.SpectateData.spMutex
import org.glavo.bot.data.Config
import org.glavo.bot.data.Permission
import org.glavo.bot.data.Player
import java.io.IOException
import java.lang.IllegalArgumentException
import java.lang.StringBuilder
import java.nio.file.Files
import java.nio.file.Path
import java.util.regex.Pattern
import kotlin.system.exitProcess
import kotlin.math.floor

@Suppress("EXPERIMENTAL_API_USAGE")
enum class Command(val level: Int = Permission.DefaultLevel) {
    Help("#help", action = { it.subject.sendMessage(it.message.quote() + "请查看 https://gitee.com/Glavo/meow-bot") }),
    IPAddress("#ip", action = {
        try {
            it.subject.sendMessage(it.message.quote() + serverIp())
        } catch (e: IOException) {
            it.subject.sendMessage(it.message.quote() + "获取服务器IP失败")
        }
    }),
    PlayerList("#list", action = {
        it.subject.sendMessage(it.message.quote() + evalCommand("list"))
    }),
    WhiteListAdd(level = 3) {
        private val command = "#whitelist "

        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)) return false
            val n = content.substring(command.length).trim()
            withCheck(event) {
                if (n.isEmpty()) {
                    event.subject.sendMessage(event.message.quote() + "需要指定角色名")
                } else {
                    event.subject.sendMessage(event.message.quote() + evalCommand("whitelist add $n"))
                }
            }
            return true
        }
    },
    Ban(level = 3) {
        private val command = "#ban "

        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)) return false
            val n = content.substring(command.length).trim()
            withCheck(event) {
                if (n.isEmpty()) {
                    event.subject.sendMessage(event.message.quote() + "需要指定角色名")
                } else {
                    event.subject.sendMessage(event.message.quote() + evalCommand("ban $n"))
                }
            }
            return true
        }
    },
    Eval(level = 4) {
        private val command = "##"

        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)) return false
            withCheck(event) {
                val c = content.substring(command.length)
                if (c.isBlank()) {
                    event.subject.sendMessage(event.message.quote() + "需要指定命令")
                } else {
                    event.subject.sendMessage(event.message.quote() + evalCommand(c))
                }
            }
            return true
        }
    },
    Donate("#donate", action = {
        MainGroup.sendMessage("捐赠支持服务器请查看：https://donate.glavo.site/")
    }),
    Live("#live", action = {
        MainGroup.sendMessage("服务器开始直播了：https://live.bilibili.com/331537")
    }),
    Domain("#address", action = {
        var message = "服务器地址：${Config.ServerDomain}"
        if (org.glavo.bot.data.Mirrors.list.isNotEmpty()) {
            message += org.glavo.bot.data.Mirrors.list.joinToString("\n", "\n加速节点：\n") { "    $it" }
        }
        it.subject.sendMessage(message.toPlainText())
    }),

    TIME("#time", action = { event ->
        try {
            val res = rcon("time query daytime")
            val matcher = Pattern.compile("^The time is (?<time>[0-9]+)$").matcher(res)
            if (matcher.matches()) {
                val time = matcher.group("time").toInt().let { if (it < 24000) it else 0 }

                val hour = (time / 1000 + 6).let { if (it >= 24) it - 24 else it }
                val minute = floor((time % 1000) / 16.6).toInt()

                val a = when (hour) {
                    in 0 until 1 -> "午夜"
                    in 1 until 6 -> "凌晨"
                    in 6 until 8 -> "早晨"
                    in 8 until 11 -> "上午"
                    in 11 until 13 -> "中午"
                    in 13 until 17 -> "下午"
                    in 17 until 19 -> "傍晚"
                    in 19 until 23 -> "晚上"
                    in 23 until 24 -> "午夜"
                    else -> ""
                }

                val tip = when {
                    time < 2000 -> "美好的清晨"
                    time < 5000 -> "村民努力工作中"
                    time < 7000 -> "一起午餐吧"
                    time < 11616 -> ""
                    time < 12000 -> "天色渐晚"
                    time < 12544 -> "村民已经休息了"
                    time < 13188 -> "该睡觉了"
                    time < 22200 -> "现在外面很危险"
                    time < 22812 -> "观赏日出的好时候"
                    else -> "新的一天开始了"
                }

                val msg = buildString {
                    append("现在是%s %02d:%02d".format(a, hour, minute))
                    if (tip.isEmpty()) {
                        append('。')
                    } else {
                        append('，')
                        append(tip)
                        append('。')
                    }
                }
                event.subject.sendMessage(msg)
            } else {
                event.subject.sendMessage(event.message.quote() + res)
            }

        } catch (ex: Throwable) {
            event.subject.sendMessage(event.message.quote() + "连接服务器失败")
        }
    }),

    //region Chat
    Chat("#chat", action = {
        it.subject.sendMessage(
            it.message.quote() + ("连续聊天模式：当用户在此模式之下时，其在群内发言会被默认转发至服务器中（以“;;”开头的消息，以及以“#”开头的命令不会被转发）。\n" +
                    "使用方法：\n" +
                    "    发送“#chat on”命令进入连续聊天模式；\n" +
                    "    发送“#chat off”命令退出连续聊天模式。\n" +
                    "    发送“#chat status”命令查询当前是否在连续聊天模式下；\n" +
                    "    发送“#chat list”命令查看所有处于连续聊天模式下的用户。"
                    )
        )
    }),
    ChatOn("#chat on", action = {
        if (ChatOffList.remove(it.sender.id)) {
            it.subject.sendMessage(it.message.quote() + "您已处于连续聊天模式下")
        } else {
            it.subject.sendMessage(it.message.quote() + "已进入连续聊天模式")
        }
    }),
    ChatOff("#chat off", action = {
        if (ChatOffList.add(it.sender.id)) {
            it.subject.sendMessage(it.message.quote() + "您不处于连续聊天模式下")
        } else {
            it.subject.sendMessage(it.message.quote() + "已退出连续聊天模式")
        }
    }),
    ChatStatus("#chat status", action = {
        if (it.sender.id in ChatOffList) {
            it.subject.sendMessage(it.message.quote() + "您当前不处于连续聊天模式下")
        } else {
            it.subject.sendMessage(it.message.quote() + "您当前处于连续聊天模式下")
        }
    }),
    ChatListAll("#chat list", action = {
        val l = ChatOffList.list
        if (l.isEmpty()) {
            it.subject.sendMessage(it.message.quote() + "当前所有用户都处于连续聊天模式")
        } else {
            it.subject.sendMessage(
                it.message.quote() + l.joinToString(
                    separator = "\n",
                    prefix = "当前不处于连续聊天模式的用户：\n"
                ) { qq ->
                    Player[qq]?.let { p ->
                        val name = p.names.firstOrNull()
                        if (name != null) {
                            return@joinToString "    $name"
                        }
                    }
                    MainGroup[qq]?.let { member ->
                        return@joinToString "    " + member.nameCardOrNick
                    }

                    "    $qq"
                })
        }
    }),

    //endregion

    //region Stop
    StopAll("#stop all", level = 4, action = {
        StopServer.action!!.invoke(it)
        StopBot.action!!.invoke(it)
    }),
    StopBot("#stop bot", level = 4, action = {
        it.subject.sendMessage(it.message.quote() + "正在关闭机器人")
        botStopped = true
        delay(200L)
        bot.close()
        exitProcess(0)
    }),
    StopServer("#stop server", level = 4, action = {
        val p = Path.of(System.getenv("MC_STOP_FILE"))
        if (Files.notExists(p)) {
            Files.createFile(p)
        }
        it.subject.sendMessage(it.message.quote() + evalCommand("stop"))
    }),

    //endregion Stop

    //region Spectate

    SpectateOn(level = 4) {
        private val command = "#sp on"

        private suspend fun startSpectate() {
            bot.launch {
                while (true) {
                    spMutex.lock()
                    try {
                        if (!SpectateData.on) {
                            break
                        }
                        if (SpectateData.target == null) {
                            SpectateData.on = false
                            break
                        }

                        val a1 = rcon("tp Glavo ${SpectateData.target}")
                        val a2 = rcon("spectate ${SpectateData.target} Glavo")
                        CommandGroup.sendMessage(a1 + "\n" + a2)
                    } catch (e: Throwable) {
                        CommandGroup.sendMessage("连接服务器失败")
                        SpectateData.on = false
                    } finally {
                        spMutex.unlock()
                    }
                    delay(8000L)
                }
            }
        }

        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)) return false
            withCheck(event) {
                val c = content.substring(command.length).trim()
                if (c.isBlank()) {
                    event.subject.sendMessage(event.message.quote() + "请指定被附身者的名称")
                    return true
                }
                if (!testServer()) {
                    event.subject.sendMessage(event.message.quote() + "服务器未运行")
                    return true
                }
                Player.All.firstOrNull { it.names.contains(c) }?.let {
                    spMutex.withLock {
                        SpectateData.target = c
                        if (SpectateData.on) {
                            return true
                        }
                        SpectateData.on = true
                    }
                    startSpectate()
                    return true
                }

                Player.All.firstOrNull { it.nicknames.contains(c) }?.let {
                    val name = it.names.firstOrNull()
                    if (name == null) {
                        event.subject.sendMessage(event.message.quote() + "指定的玩家未登入过服务器")
                    } else {
                        spMutex.withLock {
                            SpectateData.target = name
                            if (SpectateData.on) {
                                return true
                            }
                            SpectateData.on = true
                        }
                        startSpectate()
                    }
                    return true
                }
                event.subject.sendMessage(event.message.quote() + "无法找到命令指定的对象")
            }
            return true
        }
    },
    SpectateOff("#sp off", level = 4, action = {
        spMutex.withLock {
            if (SpectateData.on) {
                SpectateData.on = false
                it.subject.sendMessage(it.message.quote() + "已退出持续附身状态")
            } else {
                it.subject.sendMessage(it.message.quote() + "当前未处于持续附身状态")
            }
        }
    }),
    SpectateStatus("#sp status", level = 1, action = {
        SpectateData.spMutex.withLock {
            if (SpectateData.on) {
                it.subject.sendMessage(it.message.quote() + "当前附身的对象为：${SpectateData.target}")
            } else {
                it.subject.sendMessage(it.message.quote() + "当前未处于持续附身状态")
            }
        }
    }),
    Spectate(level = 1) {
        private val command = "#sp"
        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)
                || content.startsWith("#sp on")
                || content.startsWith("#sp off")
                || content.startsWith("#sp status")
            ) return false
            withCheck(event) {
                val c = content.substring(command.length).trim()
                if (c.isBlank()) {
                    event.subject.sendMessage(event.message.quote() + "请指定被附身者的名称")
                    return true
                }

                suspend fun spectate(i: String) {
                    event.subject.sendMessage(
                        event.message.quote() + evalCommands(
                            "tp Glavo $i",
                            "spectate $i Glavo"
                        )
                    )
                }

                Player.All.firstOrNull { it.names.contains(c) }?.let {
                    spectate(c)
                    return true
                }

                Player.All.firstOrNull { it.nicknames.contains(c) }?.let {
                    val name = it.names.firstOrNull()
                    if (name == null) {
                        event.subject.sendMessage(event.message.quote() + "指定的玩家未登入过服务器")
                    } else {
                        spectate(name)
                    }
                    return true
                }

                event.subject.sendMessage(event.message.quote() + "无法找到命令指定的对象")
            }
            return true
        }
    },

    //endregion

    NAS(level = Permission.OwnerLevel) {
        private val command = "#nas "
        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)) return false
            val n = content.substring(command.length).trim()
            withCheck(event) {
                val cmd = when (n) {
                    "status", "on", "reset" -> listOf("power", n)
                    "off", "soft" -> listOf("power", "soft")
                    else -> n.split(' ')
                }

                when (val res = ipmi(cmd)) {
                    IPMIResult.TimeOut -> event.subject.sendMessage(event.message.quote() + "执行命令超时")
                    is IPMIResult.Success -> event.subject.sendMessage(event.message.quote() + res.message)
                    is IPMIResult.Failed -> event.subject.sendMessage(event.message.quote() + "执行命令失败：${res.message}")
                }
            }
            return true
        }
    },

    RELOAD("#reload", level = 4, action = {
        try {
            org.glavo.bot.data.Data.reload()
            it.subject.sendMessage(it.message.quote() + "已重新加载数据")
        } catch (e: Throwable) {
            it.subject.sendMessage(it.message.quote() + "加载数据失败")
            bot.logger.warning("加载数据失败", e)
        }
    }),

    LOOKUP {
        private val command = "#player "
        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            if (!content.startsWith(command)) return false
            val name = content.substring(command.length).trim()
            withCheck(event) {
                val player = Player.search(name)
                if (player == null) {
                    event.subject.sendMessage(message.quote() + "未找到用户")
                } else {
                    val res = buildString {
                        append("用户 QQ：").append(player.qq)
                        if (player.names.isNotEmpty()) {
                            append("\n用户名：")
                            if (player.names.size == 1) {
                                append(player.names.first())
                            } else {
                                player.names.joinTo(this, "\n", "\n") { "    $it" }
                            }
                        }
                        if (player.nicknames.isNotEmpty()) {
                            append("\n昵称：${player.nicknames.first()}")
                        }
                    }

                    event.subject.sendMessage(message.quote() + res)
                }
            }
            return true
        }
    },

    Test("#test", action = {
        val builder = StringBuilder()
        builder.append("喵呜呜二号在线\n")
        val l = serverList()
        when {
            l != null -> {
                builder.append("服务器状态：正常\n")
                /*
                builder.append("服务器IP：")
                    .append(
                        try {
                            serverIp()
                        } catch (e: IOException) {
                            "获取失败"
                        }
                    ).append('\n')
                 */
                if (l.isEmpty()) {
                    builder.append("在线用户：（无）")
                } else {
                    builder.append("在线用户：$l")
                }
            }

            rebooting.get() -> {
                builder.append("服务器状态：重启中")
            }

            else -> {
                builder.append("服务器状态：停止")
            }
        }
        it.subject.sendMessage(it.message.quote() + builder.toString())
    }),
    Status("#status", action = {
        it.subject.sendMessage("喵呜呜二号在线")
    }),
    Forward {
        private val sayPattern = Pattern.compile("(?<name>$NamePattern): ")

        private val colorJson = buildJsonObject {
            put("text", "")
            put("color", "#d3d3d3")
        }

        override suspend fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
            var rmPre = true
            var auto = false
            if (!content.startsWith('>') && !content.startsWith('＞')) {
                if (event.sender.id !in ChatOffList && (event as? GroupMessageEvent)?.group == MainGroup) {
                    if (content.startsWith('#')
                        || content.startsWith(";;")
                        || content.startsWith("；；")
                        || message.any {
                            when (it) {
                                is RichMessage -> true
                                else -> false
                            }
                        }
                        || message.none {
                            when (it) {
                                is PlainText,
                                is At,
                                is AtAll,
                                is Face,
                                is PokeMessage,
                                is VipFace,
                                is Image,
                                is FlashImage,
                                is Audio,
                                is FileMessage -> true

                                else -> false
                            }
                        }
                    ) {
                        return false
                    }
                    auto = true
                    rmPre = false
                } else {
                    return false
                }
            }

            forward(event, message, auto, rmPre)
            return true
        }

    }
    ;

    private var commandName: String? = null
    private var action: (suspend (MessageEvent) -> Unit)? = null

    constructor(commandName: String, level: Int = 0, action: suspend (MessageEvent) -> Unit) : this(level) {
        this.commandName = commandName
        this.action = action
    }

    open suspend operator fun invoke(event: MessageEvent, message: MessageChain, content: String): Boolean {
        if (commandName == null) {
            event.bot.logger.warning("$this 命令未实现")
            return false
        }
        if (content != commandName) return false
        withCheck(event) {
            action!!.invoke(event)
        }
        return true
    }

    protected suspend inline fun withCheck(event: MessageEvent, action: () -> Unit) {
        val p = Permission.ofQQ(event.sender.id)
        if (p.any { it is Permission.CommandPermission && it.command == this }) {
            action()
        } else {
            if (p === Permission.Anonymous) {
                event.subject.sendMessage(event.message.quote() + "匿名用户无权执行该命令")
            } else {
                event.subject.sendMessage(event.message.quote() + "您无权执行该命令")
            }
        }
    }

    companion object {
        val All: List<Command> = Command.values().asList()

    }
}